package org.yun.seckill;

import com.alibaba.fastjson.JSON;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.yun.biz.model.Order;
import org.yun.config.IConfig;
import org.yun.constants.Constant;
import org.yun.constants.RedisConstant;
import org.yun.util.CommonUtil;
import org.yun.util.RedisUtil;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;

import static org.yun.constants.Constant.*;
import static org.yun.constants.RedisConstant.*;
import static org.yun.util.RedisUtil.SEC_KILL_SCRIPT;

/**
 * @ProjectName: no-concurrent
 * @ClassName: UltimateStrategy
 * @Description: nginx限流+guava限流+（黑白名单）+bitmap/redis是否参加过+redis扣除库存+同步redis
 * @Author: liyunfeng31
 * @Date: 2020/10/4 23:22
 */
@SuppressWarnings("UnstableApiUsage")
@Slf4j
@Component
public class UltimateStrategy implements SeckillStrategy {

    @Resource
    private IConfig config;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private RabbitTemplate rabbitTemplate;


    RateLimiter limiter = RateLimiter.create(200.0);


    @SuppressWarnings("unchecked")
    @Override
    public int secKill(Long userId, Long skuId,Long activityId,Integer num, String ip) {
        // 令牌限流
        if(!limiter.tryAcquire()){
            return 0;
        }
        // 校验活动
        Map<Object,Object> activity = JSON.parseObject(config.getActivity(activityId), HashMap.class);
        boolean legal = CommonUtil.checkActivity(activity, true);
        if(!legal){
            // 不合法的丢进去redis 配置lua动态黑名单
            redisUtil.sSet(BLACKLIST, ip);
            return 0;
        }
        // 本地校验有无库存
        String skuAct = skuId +":"+ activityId;
        List<String> noStocks = config.getNoStockSkuList(String.class);
        if(!CollectionUtils.isEmpty(noStocks) && noStocks.contains(skuAct)){
            return 0;
        }

        // 如需预约则校验校
        if(SUCCESS.equals(activity.get(APPOINTMENT))){
            long remove = redisUtil.setRemove(APPOINT_UUID_IDS + skuAct, userId);
            if(remove == 0){ return 0; }
        }

        // 执行秒杀脚本
        String stockKey = STOCK + skuAct;
        String userIdsKey = USER_IDS + skuAct;
        Long result = redisUtil.executeScript(SEC_KILL_SCRIPT, Arrays.asList(stockKey, userIdsKey), userId.toString(), num.toString());

        //参加过了
        if(PARTICIPATED.equals(result)){
            log.info("userId:{} has join this activity:{}", userId, activityId);
            return 0;
        }else if(NO_STOCK_.equals(result)){
            publishNoStocks(skuAct, noStocks);
            return 0;
        }
        // 抢单成功
        Order order = Order.initOrder(userId,skuId,activityId, BigDecimal.TEN);
        if(config.getSnapshotSwitch()){
            // 保存快照提供回查兜底
            redisUtil.set(SNAPSHOT + order.getOrderId(), order);
        }
        // mq发送下游履约系统 结构化存储
        //rabbitTemplate.convertAndSend("topicExchange", "topic.record", order);
        return 1;
    }




    private void publishNoStocks(String skuAct, List<String> noStocks){
        //库存不足
        log.info("skuAct:{} no stock", skuAct);
        boolean lock = redisUtil.lock(PUBLISH_LOCK + skuAct, VAL, 10);
        if(lock){
            noStocks.add(skuAct);
            config.publishConfig(NO_STOCK_SKU_LIST+JSON.toJSONString(noStocks));
            redisUtil.unLock(PUBLISH_LOCK + skuAct,VAL);
        }
    }

}
